package ftx

import (
	"encoding/json"
	"hash/crc32"
	"strconv"
	"strings"
	"sync"
	"time"
	"wscoin/common"

	log "github.com/astaxie/beego/logs"
	jsoniter "github.com/json-iterator/go"
)

/**
 * 封装数据的解析
 * 封装数据的构造
**/

// 处理数据序列化和反序列化
// 相比内置序列化提高了性能和内存的开销
var jsonIteratorr = jsoniter.ConfigCompatibleWithStandardLibrary

// 详情
type Channel struct {
	Event string `json:"e"`
	E     int64  `json:"E"`
}

// 数据解析
type RecvMessage struct {
	Time    int64  `json:"time"`
	Event   string `json:"event"`
	Channel string `json:"channel"`
}

// 接收数据工厂
func NewRecvMessage(message []byte) *RecvMessage {

	recvMsg := new(RecvMessage)

	err := json.Unmarshal(message, &recvMsg)
	if err != nil {
		log.Info("[exchange-ftx-model] Unmarshal err ", err)
	}

	return recvMsg

}

// 判断数据是否正常
// 数据是否存在异常情况
// 数据是否为空
func (r RecvMessage) IsValid() bool {
	return r.Channel == ""
}

// Depth-Quant
type Bid []interface{}
type Ask []interface{}

type DepthDetail struct {
	CheckSum uint32  `json:"checksum"`       // 1583509109
	Time     float64 `json:"time"`           // 1639984188.9590483
	Bids     []Bid   `json:"bids,omitempty"` // b
	Asks     []Ask   `json:"asks,omitempty"` // a
}

type Depth struct {
	Channel   string        `json:"channel"`
	Detail    *DepthDetail  `json:"data"`
	Type      string        `json:"type"`
	FTXMakret string        `json:"market"`
	Action    string        `json:"action"`
	Market    common.Market `json:"m"` // 这里也是FTX market
}

// Unmarshal ---> recv depth
func (d *Depth) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Bids     interface{} `json:"bids"`
		Asks     interface{} `json:"asks"`
		Time     float64     `json:"ts"`
		MarketN  string      `json:"market"`
		Symbol   string      `json:"symbol"`
		CheckSum uint32      `json:"checksum"`
		Type     string      `json:"type"`
	}{
		Bids:     BidsFormat(d.Detail.Bids), // [{"price":'', "amount":'', "type":""}]
		Asks:     AsksFormat(d.Detail.Asks), // [{"price":'', "amount":'', "type":""}]
		Time:     d.Detail.Time,
		MarketN:  d.Market.Symbol,
		Symbol:   d.FTXMakret,
		CheckSum: d.Detail.CheckSum,
		Type:     d.Type,
	})

}

// 解析的Depth数据是否有效
func (d *Depth) IsValid() bool {
	isCheck := d.Detail != nil && (len(d.Detail.Asks) != 0 ||
		len(d.Detail.Bids) != 0)
	return isCheck
}

// Update-Market
func (d *Depth) MarketAssign(symbol string) {
	d.Market.MarketAssign(symbol)
}

type KlineDetail struct {
	Open   string      `json:"o"`
	High   string      `json:"h"`
	Low    interface{} `json:"l"`
	Close  string      `json:"c"`
	Volume string      `json:"v"`
	Symbol string      `json:"n"`
	Status string      `json:"status"`
}

type Kline struct {
	Channel  string        `json:"channel"`
	KlineDiv *KlineDetail  `json:"result"`
	Time     int64         `json:"time"`
	Market   common.Market `json:"m"`
}

// Kline
func (k *Kline) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Open    string      `json:"open"`
		High    string      `json:"high"`
		Low     interface{} `json:"low"`
		Close   string      `json:"close"`
		Volume  string      `json:"volume"`
		Time    int64       `json:"ts"`
		MarketN string      `json:"market"`
	}{
		Open:    k.KlineDiv.Open,
		High:    k.KlineDiv.High,
		Low:     k.KlineDiv.Low,
		Close:   k.KlineDiv.Close,
		Volume:  k.KlineDiv.Volume,
		Time:    k.Time,
		MarketN: k.Market.Symbol,
	})
}

type TickerDetail struct {
	Time      float64 `json:"time"`
	LastPrice float64 `json:"last"`
	BidPrice  float64 `json:"bid"`
	BidQty    float64 `json:"bidSize"`
	AskPrice  float64 `json:"ask"`
	AskQty    float64 `json:"askSize"`
}

type Ticker struct {
	Channel      string        `json:"channel"`
	TickerDetail *TickerDetail `json:"data"`
	Event        string        `json:"type"`
	Symbol       string        `json:"market"`
	Market       common.Market `json:"m"`
}

// Ticker Marshal
func (k *Ticker) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Symbol    string  `json:"symbol"`
		LastPrice float64 `json:"lastPrice"`
		BidPrice  float64 `json:"bidPrice"`
		BidQty    float64 `json:"bidQty"`
		AskPrice  float64 `json:"askPrice"`
		AskQty    float64 `json:"askQty"`
		Time      float64 `json:"ts"`
		MarketN   string  `json:"market"`
	}{
		Symbol:    k.Symbol,
		LastPrice: k.TickerDetail.LastPrice,
		BidPrice:  k.TickerDetail.BidPrice,
		BidQty:    k.TickerDetail.BidQty,
		AskPrice:  k.TickerDetail.AskPrice,
		AskQty:    k.TickerDetail.AskQty,
		MarketN:   k.Market.Symbol,
		Time:      k.TickerDetail.Time,
	})
}

// 解析的Depth数据是否有效
func (t *Ticker) IsValid() bool {
	isCheck := t.TickerDetail != nil
	return isCheck
}

// Update-Market
func (t *Ticker) MarketAssign(symbol string) {
	t.Market.MarketAssign(symbol)
}

// quotation
type QuotationDetail struct {
	LastPrice   interface{} `json:"price"`
	CreateTime  interface{} `json:"time"`
	Side        string      `json:"side"`
	Volume      interface{} `json:"size"`
	Liquidation interface{} `json:"liquidation"`
	OrderID     int64       `json:"id"`
}

// Quotation Marshal
func (q *QuotationDetail) MarshalJSON() ([]byte, error) {

	return json.Marshal(struct {
		LastPrice   interface{} `json:"lastPrice"`
		CreateTime  interface{} `json:"time"`
		Side        string      `json:"side"`
		Volume      interface{} `json:"quotation"`
		Liquidation interface{} `json:"liquidation"`
		OrderID     int64       `json:"id"`
	}{
		LastPrice:   q.LastPrice,
		CreateTime:  q.CreateTime,
		Side:        q.Side,
		Volume:      q.Volume,
		Liquidation: q.Liquidation,
		OrderID:     q.OrderID,
	})
}

type Quotation struct {
	Channel         string            `json:"channel"`
	QuotationDetail []QuotationDetail `json:"data"`
	Time            int64             `json:"time"`
	FTXMakret       string            `json:"market"`
	Type            string            `json:"type"`
	Market          common.Market     `json:"mk"`
}

// Quotation Marshal
func (q *Quotation) MarshalJSON() ([]byte, error) {

	return json.Marshal(struct {
		Symbol  string      `json:"symbol"`
		Time    int64       `json:"ts"`
		Type    string      `json:"type"`
		MarketN string      `json:"market"`
		Data    interface{} `json:"data"`
	}{
		Symbol:  q.FTXMakret,
		Time:    time.Now().UnixNano() / 1e6,
		MarketN: q.Market.Symbol,
		Data:    q.QuotationDetail,
		Type:    q.Type,
	})
}

// 解析的Depth数据是否有效
func (q *Quotation) IsValid() bool {
	isCheck := q.QuotationDetail != nil
	return isCheck
}

func (q *Quotation) MarketAssign(symbol string) {
	q.Market.MarketAssign(symbol)
}

//  处理depth 数据格式
type OrderBook struct {
	Amount float64 `json:"amount,omitempty"`
	Price  float64 `json:"price,omitempty"`
}

type CacheDepth struct {
	Bids     []OrderBook `json:"bids"`
	Asks     []OrderBook `json:"asks"`
	Time     float64     `json:"ts"`
	Market   string      `json:"market"`
	Symbol   string      `json:"symbol"`
	CheckSum uint32      `json:"checksum"`
	Mu       sync.Mutex
	control  bool
}

func (d *CacheDepth) CheckCRC32() bool {

	var bidprice, bidamount, askprice, askamount, crc32NumStr string
	fmttype := 0.0001
	if len(d.Bids) > len(d.Asks) {
		for i := 0; i < len(d.Bids); i++ {
			// a + b
			if d.Bids[i].Price < fmttype && d.Bids[i].Price > 0 {
				bidprice = strconv.FormatFloat(d.Bids[i].Price, 'e', -1, 64)
			} else {
				bidprice = strconv.FormatFloat(d.Bids[i].Price, 'f', -1, 64)
				if !strings.Contains(bidprice, ".") {
					bidprice += ".0"
				}

			}

			if d.Bids[i].Amount < fmttype && d.Bids[i].Amount > 0 {
				bidamount = strconv.FormatFloat(d.Bids[i].Amount, 'e', -1, 64)

			} else {
				bidamount = strconv.FormatFloat(d.Bids[i].Amount, 'f', -1, 64)
				if !strings.Contains(bidamount, ".") {
					bidamount += ".0"
				}
			}

			if i < len(d.Asks) {

				if d.Asks[i].Price < fmttype && d.Asks[i].Price > 0 {
					askprice = strconv.FormatFloat(d.Asks[i].Price, 'e', -1, 64)
				} else {
					askprice = strconv.FormatFloat(d.Asks[i].Price, 'f', -1, 64)
					if !strings.Contains(askprice, ".") {
						askprice += ".0"
					}
				}

				if d.Asks[i].Amount < fmttype && d.Asks[i].Amount > 0 {
					askamount = strconv.FormatFloat(d.Asks[i].Amount, 'e', -1, 64)

				} else {
					askamount = strconv.FormatFloat(d.Asks[i].Amount, 'f', -1, 64)
					if !strings.Contains(askamount, ".") {
						askamount += ".0"
					}
				}

				if len(crc32NumStr) > 0 {
					crc32NumStr = strings.Join([]string{crc32NumStr, bidprice, bidamount, askprice, askamount}, ":")
				} else {
					crc32NumStr = strings.Join([]string{bidprice, bidamount, askprice, askamount}, ":")
				}

			} else {
				if len(crc32NumStr) > 0 {
					crc32NumStr = strings.Join([]string{crc32NumStr, bidprice, bidamount}, ":")
				} else {
					crc32NumStr = strings.Join([]string{bidprice, bidamount}, ":")
				}
			}
		}
	} else {
		for i := 0; i < len(d.Asks); i++ {
			// a + b
			if d.Asks[i].Price < fmttype && d.Asks[i].Price > 0 {
				askprice = strconv.FormatFloat(d.Asks[i].Price, 'e', -1, 64)
			} else {
				askprice = strconv.FormatFloat(d.Asks[i].Price, 'f', -1, 64)
				if !strings.Contains(askprice, ".") {
					askprice += ".0"
				}
			}

			if d.Asks[i].Amount < fmttype && d.Asks[i].Amount > 0 {
				askamount = strconv.FormatFloat(d.Asks[i].Amount, 'e', -1, 64)

			} else {
				askamount = strconv.FormatFloat(d.Asks[i].Amount, 'f', -1, 64)
				if !strings.Contains(askamount, ".") {
					askamount += ".0"
				}
			}

			if i < len(d.Bids) {

				if d.Bids[i].Price < fmttype && d.Bids[i].Price > 0 {
					bidprice = strconv.FormatFloat(d.Bids[i].Price, 'e', -1, 64)
				} else {
					bidprice = strconv.FormatFloat(d.Bids[i].Price, 'f', -1, 64)
					if !strings.Contains(bidprice, ".") {
						bidprice += ".0"
					}
				}

				if d.Bids[i].Amount < fmttype && d.Bids[i].Amount > 0 {
					bidamount = strconv.FormatFloat(d.Bids[i].Amount, 'e', -1, 64)

				} else {
					bidamount = strconv.FormatFloat(d.Bids[i].Amount, 'f', -1, 64)
					if !strings.Contains(bidamount, ".") {
						bidamount += ".0"
					}
				}

				if len(crc32NumStr) > 0 {
					crc32NumStr = strings.Join([]string{crc32NumStr, bidprice, bidamount, askprice, askamount}, ":")
				} else {
					crc32NumStr = strings.Join([]string{bidprice, bidamount, askprice, askamount}, ":")
				}
			} else {
				if len(crc32NumStr) > 0 {
					crc32NumStr = strings.Join([]string{crc32NumStr, askprice, askamount}, ":")
				} else {
					crc32NumStr = strings.Join([]string{askprice, askamount}, ":")
				}
			}
		}
	}

	return d.CheckSum == CRC32(crc32NumStr)
}

func (d *CacheDepth) AppendBids(bids ...OrderBook) {
	d.Bids = append(d.Bids, bids...)

}

func (d *CacheDepth) AppendBidsSlice(num int, bids ...OrderBook) {
	d.Bids = append(d.Bids[:num], bids...)

}

func (d *CacheDepth) UpdateBidsAmount(num int, amount float64) {
	d.Bids[num].Amount = amount
}

func (d *CacheDepth) AppendAsks(asks ...OrderBook) {
	d.Asks = append(d.Asks, asks...)
}

func (d *CacheDepth) AppendAsksSlice(num int, asks ...OrderBook) {
	d.Asks = append(d.Asks[:num], asks...)
}

func (d *CacheDepth) UpdateAsksAmount(num int, amount float64) {
	d.Asks[num].Amount = amount
}

// 处理历史成交的数据格式
type CacheTrade struct {
	Symbol    string `json:"symbol"`
	LastPrice string `json:"lastPrice"`
	Time      int64  `json:"ts"`
	Quotation string `json:"quotation"`
	Market    string `json:"market"`
	TradeID   int64  `json:"id"`
	BuyId     int64  `json:"buyId"`
	SellId    int64  `json:"sellId"`
	Side      string `json:"side"`
	mu        sync.Mutex
}

func CRC32(str string) uint32 {
	return crc32.ChecksumIEEE([]byte(str))
}
